import { Injectable } from '@nestjs/common';
import * as cheerio from 'cheerio';
import {
  FlightInfo,
  FlightsByAirplane,
  FlightSegment,
  FlightTime,
  StateValues,
} from 'src/flight/interfaces/flight.interface';
import { chooseTime, parseTimeStr } from 'src/utils/time-parse';

@Injectable()
export class HtmlParseService {
  // 获取网页状态值
  parseStateValues = (htmlStr: string): StateValues => {
    const $ = cheerio.load(htmlStr);
    const viewState = $('#__VIEWSTATE').attr('value');
    const viewEvent = $('#__EVENTVALIDATION').attr('value');
    if (!viewState || !viewEvent) {
      throw new Error('Could not parse viewState or viewEvent');
    }
    return {
      viewState,
      viewEvent,
    };
  };

  /**
   * 从HTML字符串中解析所有有效数据行，并返回航班信息。
   * @param htmlStr - 包含航班信息的HTML字符串。
   * @returns 包含所有航班信息的数组。
   */
  parseTable = (htmlStr: string, date: string): FlightInfo[] => {
    const $ = cheerio.load(htmlStr);
    const flightsByAirplane: FlightsByAirplane[] = [];
    // 取出表格所有行，包含表头
    const table = $('table#GridView2').find('tr');
    // 取出奇数行
    const oddRows = table.filter((i, el) => i % 2 === 1);
    oddRows.each((_, el) => {
      const row = $(el).find('td');
      flightsByAirplane.push(this.parseFlightsByAirplane(row, date));
    });
    // console.log(flightsByAirplane);
    return this.creatFlightsInfoList(flightsByAirplane, date);
  };

  /**
   * 从HTML行中解析单个飞机当天的航班信息。
   * @param row - 包含航班信息的HTML行。
   * @returns 包含单个航班信息的对象。
   */
  parseFlightsByAirplane = (
    row: cheerio.Cheerio<any>,
    date: string,
  ): FlightsByAirplane => {
    const flights: FlightsByAirplane = {
      regNo: '',
      model: '',
      segments: [],
    };
    flights.regNo = row.first().text().trim();
    flights.model = row.eq(1).text().trim();
    for (let i = 2; i < row.length; i += 4) {
      if (
        row
          .eq(i + 1)
          .text()
          .trim() !== ''
      ) {
        flights.segments.push(this.parseFlightSegment(row, i, date));
      }
    }

    return flights;
  };

  /**
   * 根据航班数据和日期创建航班信息列表，起飞时间与当前日期相符的航班信息。
   * @param flights 航班数据数组，包含每个航班的详细信息
   * @param date 航班日期，用于标识航班信息所属的日期
   * @returns 返回一个航班信息数组，包含每个航班的详细信息
   */
  creatFlightsInfoList = (
    flights: FlightsByAirplane[],
    date: string,
  ): FlightInfo[] => {
    const flightInfoList: FlightInfo[] = [];
    for (let i = 0; i < flights.length; i++) {
      for (let j = 0; j < flights[i].segments.length; j++) {
        const depTime = chooseTime(flights[i].segments[j].flightTime, false); // 出发时间
        // 如果出发时间的日期与当前日期不符，则跳过该航班信息
        if (depTime?.getDate() !== new Date(date).getDate()) continue;
        // 否则将该航班信息添加到列表中
        flightInfoList.push({
          flightDate: date,
          regNo: flights[i].regNo,
          flightNo: flights[i].segments[j].flightNo,
          depAirport: flights[i].segments[j].depAirport,
          arrAirport: flights[i].segments[j].arrAirport,
          model: flights[i].model,
          skdDepTime: flights[i].segments[j].flightTime.skdDepTime,
          estDepTime: flights[i].segments[j].flightTime.estDepTime,
          actDepTime: flights[i].segments[j].flightTime.actDepTime,
          skdArrTime: flights[i].segments[j].flightTime.skdArrTime,
          estArrTime: flights[i].segments[j].flightTime.estArrTime,
          actArrTime: flights[i].segments[j].flightTime.actArrTime,
        });
      }
    }
    return flightInfoList;
  };

  /**
   * 从HTML行中解析单个航班段信息。
   * @param row - 包含航班段信息的HTML行。
   * @param index - 航班段信息在行中的起始索引。
   * @returns 包含单个航班段信息的对象。
   */
  parseFlightSegment = (
    row: cheerio.Cheerio<any>,
    index: number,
    date: string,
  ): FlightSegment => {
    const segment: FlightSegment = {
      flightNo: '',
      depAirport: '',
      arrAirport: '',
      flightTime: {
        skdDepTime: null,
        estDepTime: null,
        actDepTime: null,
        skdArrTime: null,
        estArrTime: null,
        actArrTime: null,
      },
    };
    segment.depAirport = row.eq(index).text().trim();
    segment.flightNo = row
      .eq(index + 1)
      .text()
      .trim();
    segment.arrAirport = row
      .eq(index + 4)
      .text()
      .trim();
    const { skdDepTime, estDepTime, actDepTime } = this.parseFlightTime(
      row.eq(index + 2),
      date,
      true,
    );
    const { skdArrTime, estArrTime, actArrTime } = this.parseFlightTime(
      row.eq(index + 3),
      date,
      false,
    );
    segment.flightTime = {
      skdDepTime,
      estDepTime,
      actDepTime,
      skdArrTime,
      estArrTime,
      actArrTime,
    };
    return segment;
  };

  /**
   * 从HTML片段中解析航班时间信息。
   * @param flightTime - 包含航班时间信息的HTML片段。
   * @param isDeparture - 是否为出发时间。
   * @returns 包含航班时间信息的对象。
   */
  parseFlightTime = (
    flightTime: cheerio.Cheerio<any>,
    date: string,
    isDeparture: boolean,
  ): FlightTime => {
    const time: FlightTime = {
      skdDepTime: null,
      estDepTime: null,
      actDepTime: null,
      skdArrTime: null,
      estArrTime: null,
      actArrTime: null,
    };
    const dephtml = flightTime.toString();
    const $ = cheerio.load(dephtml);
    if (isDeparture) {
      let tempTime = $('font')
        .contents()
        .filter(function () {
          // nodeType === 3 表示是文本节点
          return this.nodeType === 3;
        })
        .text()
        .trim();
      time.skdDepTime = parseTimeStr(tempTime, date);
      tempTime = $('span').first().text().trim();
      time.estDepTime = parseTimeStr(tempTime, date);
      tempTime = $('span').eq(1).text().trim();
      time.actDepTime = parseTimeStr(tempTime, date);
    } else {
      let tempTime = $('font')
        .contents()
        .filter(function () {
          // nodeType === 3 表示是文本节点
          return this.nodeType === 3;
        })
        .text()
        .trim();
      time.skdArrTime = parseTimeStr(tempTime, date);
      tempTime = $('span').first().text().trim();
      time.estArrTime = parseTimeStr(tempTime, date);
      tempTime = $('span').eq(1).text().trim();
      time.actArrTime = parseTimeStr(tempTime, date);
    }
    return time;
  };
}
